use super::*;

/// JavaScript buffer wrapper
#[derive(Clone, Copy)]
pub struct Buffer<'val> {
	pub(crate) env: &'val Env,
	pub(crate) inner: sys::napi_value,
}

impl<'val> Buffer<'val> {
	/// Creates buffer copy from byte slice
	pub fn with_data<'env>(env: &'env Env, data: &[u8]) -> Result<'static, Buffer<'env>> {
		let mut ptr = core::ptr::null_mut();
		unsafe { sys::napi_create_buffer_copy(env.raw(), data.len(), data.as_ptr() as _, core::ptr::null_mut(), &mut ptr) }
			.result("napi_create_buffer_copy")
			.map(|_| Buffer { env, inner: ptr })
	}

	/// Creates empty buffer with specified length
	pub fn with_length<'env>(env: &'env Env, length: usize) -> Result<'static, Buffer<'env>> {
		let mut ret = core::ptr::null_mut();
		let mut dst: *mut core::ffi::c_void = core::ptr::null_mut();
		unsafe { sys::napi_create_buffer(env.raw(), length, &mut dst, &mut ret) }.result("napi_create_buffer")?;
		Ok(Buffer { env, inner: ret })
	}

	/// Converts to generic Value
	pub fn v(&'_ self) -> Value<'val> {
		Value::new(self.env, self.inner)
	}

	/// Creates buffer from string
	pub fn from_string<'env>(env: &'env Env, s: &str) -> Result<'static, Buffer<'env>> {
		Self::with_data(env, s.as_bytes())
	}

	/// Creates buffer from Vec
	pub fn from_vec<'env>(env: &'env Env, vec: Vec<u8>) -> Result<'static, Buffer<'env>> {
		Self::with_data(env, &vec)
	}

	/// Creates zero-filled buffer
	pub fn zeros<'env>(env: &'env Env, length: usize) -> Result<'static, Buffer<'env>> {
		let buffer = Self::with_length(env, length)?;
		unsafe {
			let data = buffer.data()?;
			core::ptr::write_bytes(data.as_mut_ptr(), 0, data.len());
		}
		Ok(buffer)
	}

	/// Gets buffer data as mutable slice
	pub fn data(&'_ self) -> Result<'static, &'val mut [u8]> {
		let mut dst: *mut core::ffi::c_void = core::ptr::null_mut();
		let mut len: usize = 0;
		unsafe { sys::napi_get_buffer_info(self.env.raw(), self.inner, &mut dst, &mut len) }.result("napi_get_buffer_info")?;
		Ok(unsafe { &mut *core::ptr::slice_from_raw_parts_mut(dst as *mut u8, len) })
	}

	/// Gets buffer length
	pub fn length(&'_ self) -> Result<'static, usize> {
		let mut dst: *mut core::ffi::c_void = core::ptr::null_mut();
		let mut len: usize = 0;
		unsafe { sys::napi_get_buffer_info(self.env.raw(), self.inner, &mut dst, &mut len) }.result("napi_get_buffer_info")?;
		Ok(len)
	}

	/// Checks if buffer is empty
	pub fn is_empty(&'_ self) -> Result<'static, bool> {
		Ok(self.length()? == 0)
	}

	/// Gets byte at index
	pub fn get(&'_ self, index: usize) -> Result<'static, u8> {
		let data = self.data()?;
		if index >= data.len() {
			return Err(Status::invalid_arg("index out of bounds"));
		}
		Ok(data[index])
	}

	/// Sets byte at index
	pub fn set(&'_ self, index: usize, value: u8) -> Result<'static, ()> {
		let data = self.data()?;
		if index >= data.len() {
			return Err(Status::invalid_arg("index out of bounds"));
		}
		data[index] = value;
		Ok(())
	}

	/// Gets buffer slice
	pub fn slice(&'_ self, start: usize, end: Option<usize>) -> Result<'static, &'val [u8]> {
		let data = self.data()?;
		let end = end.unwrap_or(data.len());
		let start = start.min(data.len());
		let end = end.min(data.len());

		if start >= end {
			return Ok(&[]);
		}

		Ok(&data[start..end])
	}

	/// Converts to Vec
	pub fn to_vec(&'_ self) -> Result<'static, Vec<u8>> {
		let data = self.data()?;
		Ok(data.to_vec())
	}

	/// Converts to UTF-8 string
	pub fn to_string(&'_ self) -> Result<'static, String> {
		let data = self.data()?;
		String::from_utf8(data.to_vec()).map_err(|_| Status::invalid_arg("buffer contains invalid UTF-8"))
	}

	/// Converts to string with lossy encoding
	pub fn to_string_lossy(&'_ self) -> Result<'static, String> {
		let data = self.data()?;
		Ok(String::from_utf8_lossy(data).into_owned())
	}

	/// Compares with another buffer
	pub fn equals(&'_ self, other: &Buffer<'val>) -> Result<'static, bool> {
		let data1 = self.data()?;
		let data2 = other.data()?;
		Ok(data1 == data2)
	}

	/// Creates copy of buffer
	pub fn copy(&'_ self) -> Result<'static, Buffer<'val>> {
		let data = self.data()?;
		Self::with_data(self.env, data)
	}

	/// Fills buffer with byte value
	pub fn fill(&'_ self, value: u8) -> Result<'static, ()> {
		let data = self.data()?;
		unsafe {
			core::ptr::write_bytes(data.as_mut_ptr(), value, data.len());
		}
		Ok(())
	}

	/// Concatenates multiple buffers
	pub fn concat<'env,'other>(env: &'env Env, buffers: &[&Buffer<'other>]) -> Result<'static, Buffer<'env>> {
		let total_len: usize = buffers.iter().map(|b| b.length().unwrap_or(0)).sum();
		let result = Self::with_length(env, total_len)?;
		let result_data = result.data()?;
		let mut offset = 0;

		for buffer in buffers {
			let data = buffer.data()?;
			if offset + data.len() > result_data.len() {
				return Err(Status::invalid_arg("buffer length mismatch"));
			}
			unsafe {
				core::ptr::copy_nonoverlapping(data.as_ptr(), result_data[offset..].as_mut_ptr(), data.len());
			}
			offset += data.len();
		}

		Ok(result)
	}

	/// Finds byte in buffer
	pub fn find(&'_ self, byte: u8) -> Result<'static, Option<usize>> {
		let data = self.data()?;
		for (i, &b) in data.iter().enumerate() {
			if b == byte {
				return Ok(Some(i));
			}
		}
		Ok(None)
	}

	/// Checks if buffer contains pattern
	pub fn contains(&'_ self, pattern: &[u8]) -> Result<'static, bool> {
		let data = self.data()?;
		Ok(data.windows(pattern.len()).any(|window| window == pattern))
	}
}

impl<'val> FromNapi<'val> for Buffer<'val> {
	/// Converts from Value to Buffer
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		if v.is_buffer()? {
			Ok(Buffer {
				env: v.env,
				inner: v.inner,
			})
		} else {
			Err(Status::invalid_arg("value is not Buffer type"))
		}
	}
}

impl<'val> IntoNapi for Buffer<'val> {
	/// Converts Buffer to Value
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		if self.env.raw() != env.raw() {
			return Err(Status::invalid_arg("self.env not eq arg env"));
		}
		Ok(Value::new(env, self.inner))
	}
}

impl<'val> From<Buffer<'val>> for &'val [u8] {
	/// Converts Buffer to byte slice
	fn from(v: Buffer<'val>) -> Self {
		v.data().unwrap()
	}
}

impl<'val> FromNapi<'val> for &'val [u8] {
	/// Converts from Value to byte slice
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		Ok(v.to::<Buffer>()?.data()?)
	}
}
